package utils;

/**
 * description: <br>
 * createTime: 2021/1/6 16:08 <br>
 *
 * @author zzh
 */

import org.apache.commons.io.IOUtils;
import utils.security.MD5Util;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Function;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;

/**
 * jarPath: jar包所在路径
 * jarFilePath: jar中想要修改文件所在的路径
 * regex：正则表达式
 * replacement：替换的字符串
 * 注意：Jar包内的jar包内的文件不适用！
 */
public class JarTool {

    private JarFile jarFile(String jarPath) throws IOException {
        File file = new File(jarPath);
        JarFile jarFile = new JarFile(file);// 通过jar包的路径 创建Jar包实例
        return jarFile;
    }

    public String read(String jarPath, String jarFilePath) throws IOException {
        JarFile jarFile = jarFile(jarPath);// 通过jar包的路径 创建Jar包实例
        JarEntry entry = jarFile.getJarEntry(jarFilePath);//通过某个文件在jar包中的位置来获取这个文件
        //创建该文件输入流
        InputStream input = jarFile.getInputStream(entry);
        return readFile(input, (buf) -> buf.toString());// 读取并修改文件内容
    }

    public byte[] read2Bytes(String jarPath, String jarFilePath) throws IOException {
        JarFile jarFile = jarFile(jarPath);// 通过jar包的路径 创建Jar包实例
        JarEntry entry = jarFile.getJarEntry(jarFilePath);//通过某个文件在jar包中的位置来获取这个文件
        //创建该文件输入流
        try(InputStream input = jarFile.getInputStream(entry);) {
            return readFile2Bytes(input);// 读取并修改文件内容
        }
    }

    private static byte[] readFile2Bytes(InputStream input)
            throws IOException {
        return IOUtils.readFully(input, input.available());
    }

    public void change(String jarPath, String jarFilePath, String regex, String replacement) throws IOException {
        JarFile jarFile = jarFile(jarPath);// 通过jar包的路径 创建Jar包实例
        change(jarFile, jarFilePath, (buf) -> buf.toString().replaceAll(regex, replacement));
    }

    public void change(JarFile jarFile, String jarFilePath, Function<StringBuilder, String> function) throws IOException {
        JarEntry entry = jarFile.getJarEntry(jarFilePath);//通过某个文件在jar包中的位置来获取这个文件
        //创建该文件输入流
        InputStream input = jarFile.getInputStream(entry);
        //获取entries集合lists
        List<JarEntry> lists = new LinkedList<>();
        Enumeration<JarEntry> entries = jarFile.entries();
        while (entries.hasMoreElements()) {
            JarEntry jarEntry = entries.nextElement();
            lists.add(jarEntry);
        }
        String s = readFile(input, function);// 读取并修改文件内容
        writeFile(lists, jarFilePath, jarFile, s);// 将修改后的内容写入jar包中的指定文件
        jarFile.close();
    }

    private static String readFile(InputStream input, Function<StringBuilder, String> function)
            throws IOException {
        InputStreamReader isr = new InputStreamReader(input);
        BufferedReader br = new BufferedReader(isr);
        StringBuilder buf = new StringBuilder();
        String line;
        while ((line = br.readLine()) != null) {
            // 此处根据实际需要修改某些行的内容
            buf.append(line);
            buf.append(System.getProperty("line.separator"));
        }
        br.close();
        return function.apply(buf);
    }

    private static void writeFile(List<JarEntry> lists, String jarFilePath,
                                  JarFile jarFile, String content) throws IOException {
        FileOutputStream fos = new FileOutputStream(jarFile.getName(), true);
        JarOutputStream jos = new JarOutputStream(fos);
        try {
            for (JarEntry je : lists) {
                if (je.getName().equals(jarFilePath)) {
                    // 将内容写入文件中
                    jos.putNextEntry(new JarEntry(jarFilePath));
                    jos.write(content.getBytes());
                } else {
                    //表示将该JarEntry写入jar文件中 也就是创建该文件夹和文件
                    jos.putNextEntry(new JarEntry(je));
                    jos.write(streamToByte(jarFile.getInputStream(je)));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭流
            jos.close();
        }
    }

    private static byte[] streamToByte(InputStream inputStream) {
        ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
        try {
            byte[] buffer = new byte[1024];
            int len;
            while ((len = inputStream.read(buffer)) != -1) {
                outSteam.write(buffer, 0, len);
            }
            outSteam.close();
            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return outSteam.toByteArray();
    }


    public static void main(String[] args) throws IOException {
//        JarTool jarTool = new JarTool();
//        jarTool.change("D:\\IDEA\\workSpace\\demo.jar"
//                , "spring/spring-aop.xml", "expression=\".*\"", "expression=\"%%\"");

//        System.out.println(MD5Util.getMd5String(new File("D:\\IDEA\\license-cloud\\json\\target\\json.jar")));//960816269F6F35C266658507508BD1DE

//        byte[] bytes = new JarTool().read2Bytes("D:\\IDEA\\license-cloud\\license-server\\target\\license-server-1.0.jar", "BOOT-INF/lib/json-1.0.jar");
//        System.out.println(MD5Util.getMd5String(bytes));
//        System.out.println(bytes.length);

        byte[] bytes = new JarTool().read2Bytes("D:\\IDEA\\license-cloud\\license-server\\target\\license-server-1.0.jar", "BOOT-INF/lib/commons-io-2.5.jar");
        //6E63195D8BF97602D3330033C54BDD05
        System.out.println(MD5Util.getMd5String(bytes));
    }
}